module logic_unit(
  input [21:0] op,

  input [63:0] src1,
  input [63:0] src2,
  input [63:0] imm,
  input [63:0] pc_i,

  output [63:0] res,
  output valid_o
);
  /*verilator no_inline_module*/ 
  wire op_slli/*verilator public_flat*/ ,op_slliw/*verilator public_flat*/ ,op_sllw/*verilator public_flat*/ ,op_srai/*verilator public_flat*/ ,op_sraiw/*verilator public_flat*/ ,
  op_sraw/*verilator public_flat*/ ,op_srli/*verilator public_flat*/ ,op_srliw/*verilator public_flat*/ ,op_srlw/*verilator public_flat*/ ,op_and/*verilator public_flat*/ ,op_andi/*verilator public_flat*/ ,
  op_or/*verilator public_flat*/ ,op_ori/*verilator public_flat*/ ,op_sll/*verilator public_flat*/ ,op_slt/*verilator public_flat*/ ,op_slti/*verilator public_flat*/ ,op_sltiu/*verilator public_flat*/ ,
  op_sltu/*verilator public_flat*/ ,op_sra/*verilator public_flat*/ ,op_srl/*verilator public_flat*/ ,op_xor/*verilator public_flat*/ ,op_xori/*verilator public_flat*/ ;

  assign {op_slli,op_slliw,op_sllw,op_srai,op_sraiw,
                          op_sraw,op_srli,op_srliw,op_srlw,op_and,
                          op_andi,op_or,op_ori,op_sll,op_slt,
                          op_slti,op_sltiu,op_sltu,op_sra,op_srl,
                          op_xor,op_xori} = op;
  wire op_w /*verilator public_flat*/ = op_slliw|op_sllw|op_sraiw|op_sraw|op_srliw|op_srlw;

  // wire [63:0] src1_w = $signed(src1);
  // wire [63:0] src2_w = $signed(src2);
  // wire [63:0] imm_w = $signed(imm);
  wire [63:0] src1_w_s = {{32{src1[31]}},src1[31:0]};
  wire [63:0] src1_w_u = {{32{1'b0}},src1[31:0]};

  wire [63:0] src1_w /*verilator public_flat*/ = {64{right_arith}}&src1_w_s | {64{!right_arith}}&src1_w_u ;
  wire [63:0] src2_w /*verilator public_flat*/ = {{32{src2[31]}},src2[31:0]};
  wire [63:0] imm_w /*verilator public_flat*/ = {{32{imm[31]}},imm[31:0]};

  wire op_value_1_src /*verilator public_flat*/ = op_slli|op_srai|op_srli|op_and|op_andi|op_or|op_ori|op_sll|op_slt|op_slti|op_sltiu|op_sltu|op_sra|op_srl|op_xor|op_xori;
  wire op_value_1_src_w /*verilator public_flat*/ = op_slliw|op_sllw|op_sraiw|op_sraw|op_srliw|op_srlw;

  wire op_value_2_src /*verilator public_flat*/ = op_and|op_or|op_sll|op_slt|op_sltu|op_sra|op_srl|op_xor;
  wire op_value_2_src_w /*verilator public_flat*/ = op_sllw|op_sraw|op_srlw;
  wire op_value_2_imm /*verilator public_flat*/ = op_slli|op_srai|op_srli|op_andi|op_ori|op_slti|op_sltiu|op_xori;
  wire op_value_2_imm_w /*verilator public_flat*/ = op_slliw|op_sraiw|op_srliw;

  wire [63:0] op_value_1 /*verilator public_flat*/ = ({64{op_value_1_src}}&src1) | ({64{op_value_1_src_w}}&src1_w);
  wire [63:0] op_value_2 /*verilator public_flat*/ = ({64{op_value_2_src}}&src2) | ({64{op_value_2_src_w}}&src2_w) | ({64{op_value_2_imm}}&imm) | ({64{op_value_2_imm_w}}&imm_w);
  wire [5:0] shift_amount /*verilator public_flat*/ = {op_value_2[5]&(!op_w),op_value_2[4:0]};

  wire left         /*verilator public_flat*/ = op_slli|op_sllw|op_slliw|op_sll;
  wire right_arith  /*verilator public_flat*/ = op_sra|op_srai|op_sraw|op_sraiw;
  wire right_logic  /*verilator public_flat*/ = op_srl|op_srli|op_srliw|op_srlw;
  // wire lower_than   = op_slt|op_slti|op_sltiu|op_sltu;

  wire lower_res /*verilator public_flat*/ = (op_sltiu|op_sltu)&(op_value_1<op_value_2) | (op_slt|op_slti)&($signed(op_value_1) < $signed(op_value_2));
  wire signed [127:0] op_val_1_sign;
    assign op_val_1_sign[63:0] = op_value_1;
    assign op_val_1_sign[127:64] = {64{op_value_1[63]}};
  wire [63:0] shift_res /*verilator public_flat*/ = {64{left}}&(op_value_1 << shift_amount) | 
                          // {64{right_arith}}&({(op_val_1_sign) >>> shift_amount}[63:0]) | 
                          {64{right_arith}}&({$signed(op_value_1) >>> shift_amount}) | 
                          {64{right_logic}}&(op_value_1 >> shift_amount);
  wire [63:0] and_res   /*verilator public_flat*/ = {64{op_and|op_andi}}&(op_value_1&op_value_2);
  wire [63:0] or_res    /*verilator public_flat*/ = {64{op_or|op_ori}}&(op_value_1|op_value_2);
  wire [63:0] xor_res   /*verilator public_flat*/ = {64{op_xor|op_xori}}&(op_value_1^op_value_2);

  wire [63:0] res_mid = {63'b0,lower_res} | shift_res | and_res | or_res | xor_res;
  assign res = { ({32{op_w&res_mid[31]}} | {32{!op_w}}&res_mid[63:32] ) ,res_mid[31:0]};
  assign valid_o = |op;
  // wire [63:0] shift_res_1  = {64{op_value_2[0]}}(({64{left}}&{  op_value_1[63- 1:0], 1'b0}) | ({64{right_logic}}&{ 1'b0,  op_value_1[63: 1]}) | ({64{right_arith}}&{  op_value_1[63:64- 1],  op_value_1[63: 1]})) | {64{!op_value_2[0]}}&(op_value_1);
  // wire [63:0] shift_res_2  = {64{op_value_2[1]}}(({64{left}}&{ shift_res_1[63- 2:0], 2'b0}) | ({64{right_logic}}&{ 2'b0, shift_res_1[63: 2]}) | ({64{right_arith}}&{ shift_res_1[63:64- 2], shift_res_1[63: 2]})) | {64{!op_value_2[1]}}&(shift_res_1);
  // wire [63:0] shift_res_4  = {64{op_value_2[2]}}(({64{left}}&{ shift_res_2[63- 4:0], 4'b0}) | ({64{right_logic}}&{ 4'b0, shift_res_2[63: 4]}) | ({64{right_arith}}&{ shift_res_2[63:64- 4], shift_res_2[63: 4]})) | {64{!op_value_2[2]}}&(shift_res_2);
  // wire [63:0] shift_res_8  = {64{op_value_2[3]}}(({64{left}}&{ shift_res_4[63- 8:0], 8'b0}) | ({64{right_logic}}&{ 8'b0, shift_res_4[63: 8]}) | ({64{right_arith}}&{ shift_res_4[63:64- 8], shift_res_4[63: 8]})) | {64{!op_value_2[3]}}&(shift_res_4);
  // wire [63:0] shift_res_16 = {64{op_value_2[4]}}(({64{left}}&{ shift_res_8[63-16:0],16'b0}) | ({64{right_logic}}&{16'b0, shift_res_8[63:16]}) | ({64{right_arith}}&{ shift_res_8[63:64-16], shift_res_8[63:16]})) | {64{!op_value_2[4]}}&(shift_res_8);
  // wire [63:0] shift_res_32 = {64{op_value_2[5]&(!op_w)}}(({64{left}}&{shift_res_16[63-32:0],32'b0}) | ({64{right_logic}}&{32'b0,shift_res_16[63:32]}) | ({64{right_arith}}&{shift_res_16[63:64-32],shift_res_16[63:32]})) | {64{(!op_value_2[5])|(op_w)}}&(shift_res_16);
endmodule
